#ifndef A2C8E8E8_3755_4BB0_839C_46AB5DAE8F00
#define A2C8E8E8_3755_4BB0_839C_46AB5DAE8F00

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <string>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <memory>
#include <vector>
#include <unordered_map>
#include <functional>
#include "Log.hpp"

const uint16_t defaultport = 8080;
const std::string defaultip = "0.0.0.0";
const int size = 1024;

using fun_t = std::function<std::string(const std::string &, const std::string &, uint16_t)>;

Log lg;

enum
{
	Socket_ERROR = 1,
	Bind_ERROR,
	// Recvfrom_ERROR
};

class udpserver
{
public:
	udpserver(const uint16_t &port = defaultport, const std::string &ip = defaultip) : port_(port), ip_(ip)
	{
	}
	void Init()
	{
		sockfd = socket(AF_INET, SOCK_DGRAM, 0);
		if (sockfd == -1)
		{
			lg(Fatal, "get sockfd error,error string:%s", strerror(errno));
			exit(Socket_ERROR);
		}
		lg(Info, "create socket success,sockfd:%d", sockfd);

		// bind
		struct sockaddr_in server;
		bzero(&server, sizeof(server));
		server.sin_family = AF_INET; /// AF_INET -- ipv4
		server.sin_port = htons(port_);
		server.sin_addr.s_addr = inet_addr(ip_.c_str());
		// server.sin_addr.s_addr = INADDR_ANY;

		if (bind(sockfd, (const struct sockaddr *)&server, sizeof(server)) < 0)
		{
			lg(Fatal, "bind error,errno : %d,err string:%s", errno, strerror(errno));
			exit(Bind_ERROR);
		}
		lg(Info, "bind success ,errno : %d", errno);
	}
	// void Run(fun_t func)
	// {
	// 	char inbuffer[size];
	// 	isrunning = true;
	// 	while(isrunning)
	// 	{
	// 		struct sockaddr_in client;
	// 		socklen_t len = sizeof(client);
	// 		ssize_t n = recvfrom(sockfd,inbuffer,sizeof(inbuffer)-1,0,(struct sockaddr*)&client,&len);
	// 		if(n < 0){
	// 			lg(Warnning,"recvfrom error,errno:%d,err string:%s",errno,strerror(errno));
	// 			continue;
	// 		}
	// 		inbuffer[n] = 0; // '\0'

	// 		std::string info = inbuffer;
	// 		std::string clientip = inet_ntoa(client.sin_addr);
	// 		uint16_t clientport = ntohs(client.sin_port);
	// 		// std::cout << info << std::endl;
	// 		// info = "server say#" + info;
	// 		info = func(info,clientip,clientport);
	// 		// std::cout << info << std::endl;
	// 		sendto(sockfd,info.c_str(),info.size(),0,(const sockaddr*)&client,len);
	// 	}
	void checkusers(struct sockaddr_in &client, const std::string &clientip)
	{
		if (users_online.count(clientip) == 0)
		{
			users_online[clientip] = client;
			std::cout << clientip << " add into onlien" << std::endl;
		}
	}
	void Broadcast(std::string &info, const std::string &clientip, uint16_t port)
	{
		std::string msg = "[";
		msg += clientip;
		msg += ":";
		msg += std::to_string(port);
		msg += "]# ";
		msg += info;
		for (const auto &user : users_online)
		{
			sendto(sockfd, msg.c_str(), msg.size(), 0, (struct sockaddr *)(&user.second), sizeof(user.second));
			// std::cout << msg << std::endl;
		}
	}
	void Run()
	{
		char inbuffer[size];
		isrunning = true;
		while (isrunning)
		{
			inbuffer[0] = 0;
			struct sockaddr_in client;
			socklen_t len = sizeof(client);
			ssize_t n = recvfrom(sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr *)&client, &len);
			if (n > 0)
			{
				inbuffer[n] = 0; // '\0'
				std::string clientip = inet_ntoa(client.sin_addr);
				uint16_t clientport = ntohs(client.sin_port);
				checkusers(client, clientip);
				std::string info = inbuffer;
				Broadcast(info, clientip, clientport);
				// std::string clientip = inet_ntoa(client.sin_addr);
				// uint16_t clientport = ntohs(client.sin_port);
				// sendto(sockfd,info.c_str(),info.size(),0,(const sockaddr*)&client,len);
			}
			else if (n < 0)
			{
				lg(Warnning, "recvfrom error,errno:%d,err string:%s", errno, strerror(errno));
				continue;
			}
			else if (n == 0)
			{
				lg(Info, "user %s quit", inet_ntoa(client.sin_addr));
				close(sockfd);
			}
		}
	}
	~udpserver()
	{
	}

private:
	int sockfd;		 // 网络文件描述符
	uint16_t port_;	 // 服务器进程的端口号
	std::string ip_; // IP地址    ----->bind任意ip地址  0
	bool isrunning = false;
	std::unordered_map<std::string, struct sockaddr_in> users_online;
};

#endif
